package com.self.cloudmall.oms;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

/**
 * 配置中心
 * 1.命名空间：作为配置隔离用的，通常作为环境和微服务隔离用,bootstrap.properties配置命名空间ID
 * 2.配置集id：就是DataID，相当于配置文件名
 * 3.配置group: 分组，默认是DEFAULT_GROUP，通常用于环境分组
 *
 * 解决同类方法互相调用，事务不起作用的拌饭，
 *  1.@EnableAspectJAutoProxy(exposeProxy = true)
 *  2.AopContext.currentProxy()获取当前代理
 *
 * raft 协议：
 *   leader: 同步日志，发送心跳检测，处理客户端请求，
 *   follower: 响应leader同步日志，自旋，事务请求转发给leader
 *   candidate: 一个节点从follower变为candidate发起选举，其他follower响应之后变为leader
 *   流程：
 *   master选举：集群初始化默认都是follower节点，都进行自旋，谁先到达自旋时间，就转化为candidate，
 *   然后给其他所有节点发送心跳，当收到半数以上ack,就变为leader；
 *   数据同步：事务请求被转发到leader上，leader先写入日志 ，然后把数据广播给其他follower，follower写入日志 ，ack给
 *   leader,leader收到半数以上 ack，就提交commit事务，然后广播给其他follower，其他follower收到广播后，
 *   commit自己日志；
 *   故障恢复：如果发生网络故障，节点脑裂，被分为多个区域的节点，当一个区域节点达不到有效投票，
 *   将不能产生不了半数投票，所以不接受客户端请求，而一部分区域能产生能达到有效投票的，可以选出leader
 *   继续提供服务，当网络恢复后，把leader数据同步给没有同步的节点，如果同时出现多个leader，将重新就行下一轮
 *   选举，知道选出一个leader，因为follower只接受一个candidate的选票请求，
 *
 *  seata 分布式事务原理：
 *   1.事务两阶提交机制：
 *      1).业务数据和回滚日志记录在同一个本地事务提交，释放本地锁和连接资源
 *         @GlobalTransactional 定义事务范围（TM），就是指多个分支事务都在同一个方法中，在放上加上该注解；
 *         分支事务（RM）刚执行时加上本地锁，
 *         本地分支事务把业务数据和生成的undo-log日志一并提交，当要提交时，需要先从TC上获取全局锁，
 *         如果获取到才能提交，否则等待重试获取全局锁；
 *         先把自己的分支注册到TC（全局事务协调器）上，将本地事务结果上报给TC
 *         如果此时异常，直接回滚本地事务；
 *      2).异步化提交，回滚一阶段回滚日志记录进行反向补偿
 *         如果TC收到分支事务回滚请求，开启一个本地事务，通过XID和BranchId查找响应的UNDO LOG 记录，
 *         进行校验（拿 UNDO LOG 中的后镜与当前数据进行比较），
 *         根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句；
 *         提交本地事务，将本地事务结果上报给TC；
 *         如果TC收到分支事务提交请求，把请求放在一个异步任务队列中，马上返回提交结果给TC，
 *         在异步任务阶段的分支事务提交中，将异步和批量地删除相应 UNDO LOG 记录
 *
 *    2.应用
 *      1.在总的业务方法加上 @GlobalTransactional
 *      2.下载seata服务，并启动服务
 *      3.客户端配置DataSourceProxy对原来DataSource代理
 *      4.修改file.conf的service.vgroup_mapping配置，
 *      ${spring.application.name}-fescar-service-group作为服务名注册到 Seata Server上；
 *      也可以通过配置 spring.cloud.alibaba.seata.tx-service-group修改后缀，但是必须和file.conf中的配置保持一致
 *
 * 保证消息可靠性
 * 1.消息生产方和消费方采用confirm模式，并且消息持久化到磁盘
 *   可以创建消息日志表，每发送一条信息，就写入日志表中维护其状态，在confirmCallback和returnCallback以及消费后中更新状态
 * 2.消息消费的幂等性设计
 */
@EnableRedisHttpSession
@EnableRabbit
@EnableFeignClients(basePackages = "com.self.cloudmall.oms.client")
@EnableDiscoveryClient
@MapperScan("com.self.cloudmall.oms.mapper")
@SpringBootApplication
public class CloudOmsApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudOmsApplication.class, args);
    }

}
